package senzuri.acceleraudio.util;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder.AudioSource;
import android.util.Log;

public class MicrophoneReader {
  private class MicrophoneChunk {
    public short[] samples;
    public int count;
    public MicrophoneChunk(int chunkLength) {
      samples = new short[chunkLength];
    }
  }
  
  private class AudioInputTask extends AsyncTask {
    Semaphore done = new Semaphore(0);
    
    @Override protected void doInBackground() {
      Log.d("MicrophoneReader", "Audio recorder thread started");
      AudioRecord recorder = null;
      
      try {
        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
        Thread.currentThread().setName("AudioInputTask");
        
        MicrophoneChunk[] buffers = new MicrophoneChunk[256];
        for (int i = 0; i < buffers.length; i++)
          buffers[i] = new MicrophoneChunk(audioFrequency / 50);
        
        int min = AudioRecord.getMinBufferSize(audioFrequency, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
        recorder = new AudioRecord(AudioSource.MIC, audioFrequency, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, min * 10);
  
        recorder.startRecording();

        int ix = 0;
        while (!Thread.interrupted()) {
          MicrophoneChunk b = buffers[ix++ % buffers.length];
          b.count = recorder.read(b.samples, 0, b.samples.length);
          this.publishProgress(b);
        }
      
        return;
      } catch (Exception e) {
        Log.e("MicrophoneReader", "Error in the audio recorder");
        return;
      } finally {
        Log.d("MicrophoneReader", "Releasing semaphore");
        done.release();
        if (recorder != null && recorder.getState() == AudioRecord.STATE_INITIALIZED)
          recorder.stop();
      }
    }
    
    @Override protected void onProgressUpdate(Object value) {
      MicrophoneChunk c = (MicrophoneChunk)value;
      if (listener != null)
        listener.onMicrophoneData(c.samples, c.count);
    }
  }
  
  public static interface MicrophoneListener {
    public void onMicrophoneData(short[] samples, int count);
  }
  
  private AudioInputTask task;
  public MicrophoneListener listener;
  public final int audioFrequency;
  private Context context;
  
  public MicrophoneReader(Context context, int audioFrequency) {
    this.audioFrequency = audioFrequency;
    this.context = context;
  }
  
  public void setListener(MicrophoneListener listener) {
    this.listener = listener;
    if (listener == null && task != null) {
      task.interrupt();
      try {
        Log.d("MicrophoneReader", "Acquiring semaphore");
        if (!task.done.tryAcquire(1, TimeUnit.SECONDS))
          Log.e("MicrophoneReader", "Couldn't acquire semaphore!");
      } catch (InterruptedException e) {}
      task = null;
    } else if (listener != null && task == null) {
      task = new AudioInputTask();
      task.execute(context);
    }
  }
}
